/*
 *  composition 0038 util.cpp
 *  SoundController0002
 *
 *  Created by jiro on 11/09/05.
 *  Copyright 2011 jiro music. All rights reserved.
 *
 */

#include "composition 0039 util.h"

#include "../HarmonicSystem/HarmonicSystem.h"
#include "../0039/Theory0039.h"
#include "../../sc/AudioRecorder.h"
#include "../../sc/SoundGenerator.h"

using namespace sc;
using namespace sc::hs;

namespace theory0039 {

	SequenceElement::SequenceElement()
	{}

	SequenceElement::SequenceElement( sc::Note::MidiNumber number0, double harmonics0, sc::Note::MidiNumber number1, double harmonics1, 
									  double dynamics, int repeatCount )
		: frequencies(),
		  dynamics( dynamics ),
		  repeatCount( repeatCount )
	{
		frequencies[ 0 ] =  harmonics0 * WellTemperament::GetFrequency( number0 );
		frequencies[ 1 ] =  harmonics1 * WellTemperament::GetFrequency( number1 );
	}
	
	void SequenceElement::SetRepeatCount( int count )
	{
		repeatCount = count;
	}

	double SequenceElement::GetFrequency( int index ) const
	{
		return frequencies[index ];
	}

	double SequenceElement::GetDynamics() const
	{
		return dynamics;
	}

	int SequenceElement::GetRepeatCount() const
	{
		return repeatCount;
	}

	double SequenceElement::GetDuration( bool isRepeat ) const
	{
		double howlFreq = fabs( frequencies[ 0 ] - frequencies[ 1 ] );
		double duration = 1.0 / howlFreq;
		if ( isRepeat )
		{
			duration *= repeatCount;
		}
		return duration;
	}

	::sc::hs::Chord SequenceElement::ToChord() const
	{
		::sc::hs::Chord chord;
		chord.push_back( Sound( frequencies[ 0 ], dynamics ) );
		chord.push_back( Sound( frequencies[ 1 ], dynamics ) );
		return chord;
	}

	Section::Section()
	{}

	Section::Section( const SequenceElement& baseElement, int partCount )
		: elementsList( partCount )
	{
		elementsList[ 0 ].push_back( baseElement );
	}

	void Section::SetBaseElement( const SequenceElement& element )
	{
		elementsList[ 0 ].push_back( element );
	}

	void Section::SetPartCount( int count )
	{
		elementsList.resize( count );
	}

	void Section::AddSubElement( const SequenceElement& element, int partIndex )
	{
		elementsList[ partIndex ].push_back( element );
	}

	double Section::GetDuration( int partIndex, bool isRepeat ) const
	{
		const std::vector< SequenceElement >& elements = elementsList[ partIndex ];
		double duration = 0.0;
		for ( int i = 0, count = elements.size(); i < count; i++ )
		{
			duration += elements[ i ].GetDuration( isRepeat );
		}
		return duration;
	}

	double Section::GetTotalDuration() const
	{
		double duration = 0.0;
		int partCount = elementsList.size();
		for ( int i = 0; i < partCount; i++ )
		{
			double d = GetDuration( i, true );
			if ( duration < d )
			{
				duration = d;
			}
		}
		return duration;
	}

	double Section::GetMaxSubElementDuration() const
	{
		double max = 0.0;
		for ( int i = 0, partCount = elementsList.size(); i < partCount; i++ )
		{
			double duration = GetDuration( i, true );
			if ( max < duration )
			{
				max = duration;
			}
		}
		return max;
	}

	int Section::GetMinRepeatCountOfBaseElement() const
	{
		double maxDuration = GetMaxSubElementDuration();
		double baseDuration = GetDuration( 0, false );
		int repeatCount = maxDuration / baseDuration;
		if ( repeatCount != maxDuration / baseDuration )
		{
			repeatCount++;
		}
		return repeatCount;
	}

	void Section::SetRepeatCountOfBaseElement( int count )
	{
		elementsList[ 0 ][ 0 ].SetRepeatCount( count );
	}

	std::vector< std::vector< const sc::SoundElement32* > > Section::ToSoundElements( int partIndex ) const
	{
		std::vector< std::vector< const SoundElement32* > >  soundElementsList;
		const std::vector< SequenceElement >& elements = elementsList[ partIndex ];

		int count = elements.size();
		for ( size_t i = 0; i < count; i++ )
		{
			std::vector< const SoundElement32* > soundElements;
			const SequenceElement& element = elements[ i ];
			::sc::hs::Chord chord = element.ToChord();
			double duration = element.GetDuration( true );
			std::vector< const SoundElement32* > singleSoundElements;

			for ( int j = 0; j < chord.size(); j++ )
			{
				singleSoundElements = ToSoundElements( chord[ j ], duration );
				for ( int k = 0; k < singleSoundElements.size(); k++ )
				{
					soundElements.push_back( singleSoundElements[ k ] );
				}
			}
			soundElementsList.push_back( soundElements );
		}
		return soundElementsList;
	}

	const SequenceElement& Section::GetBaseElement() const
	{
		return elementsList[ 0 ][ 0 ];
	}

	const std::vector< std::vector< SequenceElement > >& Section::GetElements() const
	{
		return elementsList;
	}

	std::vector< const sc::SoundElement32* > Section::ToSoundElements( const ::sc::hs::Sound& sound, double duration ) const
	{
		std::vector< const SoundElement32* > elements;
		static int phaseShiftIndex = 0;
		phaseShiftIndex++;

		std::vector< NoteInfo > noteInfos;
		const WaveFunction& function = WaveFunctionFactory::CreateXSinX( 0.5, 2 * duration );
	//	const WaveFunction& function = WaveFunctionFactory::CreateXSinX( 1., 1.0 / ( 2.0 * duration ) );
	//	const WaveFunction& function = WaveFunctionFactory::CreateLogXSinX( 1.0, 1.0 / ( 2.0 * duration ) );
	//	const WaveFunction& function = WaveFunctionFactory::CreateSinPowerOfX( 0.0, 1.0 / ( 2.0 * duration ) );
		const WaveTable& dynamicsTable = WaveTableFactory::Create( function, sound.GetDynamics() );

	//	const WaveTable& dynamicsTable = WaveTableFactory::Create( WaveTable::Type_Sine, 1.0 / ( 2.0 * duration ), 1.0 );

		if ( ( sound.GetFrequency() != -1 ) && ( sound.GetMidiNumber() == Note::MidiNumberMute ) )
		{
			noteInfos.push_back( NoteInfo( sound.GetFrequency(), duration, sound.GetDynamics(), dynamicsTable, 0.5 * phaseShiftIndex ) );
		}
		else
		{
			double frequency = WellTemperament::GetFrequency( sound.GetMidiNumber() );
			int count = sound.GetHarmonicsCount();
			for ( int i = 1; i <= count; i++ )
			{
				if ( sound.GetWaveType() == Sound::SingleSineWave )
				{
					noteInfos.push_back( 
						NoteInfo( 
							frequency, 
							duration, 
							sound.GetHarmonicAmplitude( 1 ) * sound.GetDynamics(), 
							dynamicsTable,
							0.5 * phaseShiftIndex ) );
					break;
				}

				double amplitude = sound.GetHarmonicAmplitude( i );
				if ( amplitude == 0 )
				{
					continue;
				}

				switch ( sound.GetWaveType() )
				{
				case Sound::SawWave:
					noteInfos.push_back( 
						NoteInfo( 
							i * frequency, 
							duration, 
							sound.GetHarmonicAmplitude( i ) * sound.GetDynamics(), 
							dynamicsTable, 
							0.5 * phaseShiftIndex ) );
					break;

				case Sound::SquareWave:
					if ( i % 2 == 1 )
					{
						noteInfos.push_back(
							NoteInfo(
								i * frequency, 
								duration,
								sound.GetHarmonicAmplitude( i ) * sound.GetDynamics(), 
								dynamicsTable,
								0.5 * phaseShiftIndex ) );
					}
					break;

				case Sound::TriangleWave:
					if ( i % 2 == 1 )
					{
						noteInfos.push_back(
							NoteInfo( 
								i * frequency, 
								duration, 
								sound.GetHarmonicAmplitude( i ) * sound.GetDynamics(), 
								dynamicsTable,
								0.5 * phaseShiftIndex ) );
					}
					break;
				}
			}
		}
		elements = SoundGenerator::Generate( noteInfos );
		return elements;
	}
}